/*Z
big_kernel_lock初始状态:
    锁需求队列head------------------------------------------|   队列由尾至头，依次执行并释放锁，后一个未释放前前边的都等待。此队列无法直接遍历
                                                            |
                                                            V
    各需求状态节点 nodes[0]   nodes[1]  ...   nodes[n-1]  nodes[n]   各点并不对应固定的cpu
                   (state)^                                (无需求)  多余的无锁需求的尾节点确保节点链接指针移动（全竞争时也有无锁需求状态节点）
                           \
                            \    ...
                             |
    各节点链接指针  owner[0] | owner[1]  ...   owner[n-1]       各点对应固定的cpu
                    (node---/    ...                            node指向（当前）状态节点，锁释放时移向next节点（状态一定为无锁需求）
                     next                                       链接锁队列的下一节点
                     ipi)

    申请锁：设置状态，next指向*head，head指向*node，相当于插入队列。申请并发进行，在后一锁未释放前暂停执行
    释放锁：设置状态，node指向*next，相当于移动队列。释放动作由队列尾至头依次进行
*/

/*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#pragma once

#include <config.h>
#include <types.h>
#include <util.h>
#include <mode/machine.h>
#include <arch/model/statedata.h>
#include <smp/ipi.h>
#include <util.h>

#ifdef ENABLE_SMP_SUPPORT

/* CLH lock is FIFO lock for machines with coherent caches (coherent-FIFO lock).
 * See ftp://ftp.cs.washington.edu/tr/1993/02/UW-CSE-93-02-02.pdf */
/*Z 节点状态枚举 */
typedef enum {
    CLHState_Granted = 0,   /*Z 无需求 */
    CLHState_Pending        /*Z 等待锁 */
} clh_qnode_state_t;
/*Z 节点状态 */
typedef struct clh_qnode {
    clh_qnode_state_t value;/*Z 状态 */

    PAD_TO_NEXT_CACHE_LN(sizeof(clh_qnode_state_t));
} clh_qnode_t;
/*Z 锁队列节点  */
typedef struct clh_qnode_p {
    clh_qnode_t *node;  /*Z 指向本节点状态 */
    clh_qnode_t *next;  /*Z 指向下一节点状态 */
    /* This is the software IPI flag */
    word_t ipi;         /*Z 是否有cpu正在等待本cpu处理IPI。似乎切换回0可以通知等待cpu完成。不好：应该用bool类型 */

    PAD_TO_NEXT_CACHE_LN(sizeof(clh_qnode_t *) +
                         sizeof(clh_qnode_t *) +
                         sizeof(word_t));
} clh_qnode_p_t;
/*Z 大内核锁类型。带队列 */
typedef struct clh_lock {
    clh_qnode_t nodes[CONFIG_MAX_NUM_NODES + 1];    /*Z 各cpu节点状态 */
    clh_qnode_p_t node_owners[CONFIG_MAX_NUM_NODES];/*Z 锁需求队列链接指针 */

    clh_qnode_t *head;  /*Z 锁队列头 */
    PAD_TO_NEXT_CACHE_LN(sizeof(clh_qnode_t *));
} clh_lock_t;

extern clh_lock_t big_kernel_lock;
BOOT_CODE void clh_lock_init(void);
/*Z 是否有cpu正在等待指定cpu处理IPI */
static inline bool_t FORCE_INLINE clh_is_ipi_pending(word_t cpu)
{
    return big_kernel_lock.node_owners[cpu].ipi == 1;
}
/*Z 原子地将指定cpu节点状态，交给头，并返回头的 */
/* V 大内核锁用来保护共享资源不被并发访问所破坏 */
static inline void *sel4_atomic_exchange(void *ptr, bool_t  /*Z 不好：ptr没有使用 */
                                         irqPath, word_t cpu, int memorder) /* V memorder指定内存排序 */
{
    clh_qnode_t *prev;

    if (memorder == __ATOMIC_RELEASE || memorder == __ATOMIC_ACQ_REL) {　　/* V　设置内存屏障 */
        __atomic_thread_fence(__ATOMIC_RELEASE);
    } else if (memorder == __ATOMIC_SEQ_CST) {
        __atomic_thread_fence(__ATOMIC_SEQ_CST);
    }

    while (!try_arch_atomic_exchange_rlx(&big_kernel_lock.head,
                                         (void *) big_kernel_lock.node_owners[cpu].node,
                                         (void **) &prev)) {  /* V 尝试原子交换大内核锁状态，若失败意味着锁被其他处理器占有，直到成功 */
        if (clh_is_ipi_pending(cpu)) {/* V 将队列头设置为当前元素，在调用该函数的函数的下一步中会将新头的next指向原头 */
            /* we only handle irq_remote_call_ipi here as other type of IPIs
             * are async and could be delayed. 'handleIPI' may not return
             * based on value of the 'irqPath'. *//*Z 未获得锁的间隙处理远程调用IPI，以防对方阻塞时间过长 */
            handleIPI(CORE_IRQ_TO_IRQT(cpu, irq_remote_call_ipi), irqPath); /* V 有挂起的IPI请求，则优先处理IPI */
        }

        arch_pause();/* V　为了等待锁时更有效利用处理器资源，而不是总在读取锁的无谓的资源消耗 */
    }/* V　获取内存锁时短暂的等待，用与减少CPU的资源浪费和频繁的对锁的读取也就是内存的读取，从而减轻总线和内存控制器压力 */

    if (memorder == __ATOMIC_ACQUIRE || memorder == __ATOMIC_ACQ_REL) {
        __atomic_thread_fence(__ATOMIC_ACQUIRE);
    } else if (memorder == __ATOMIC_SEQ_CST) {
        __atomic_thread_fence(__ATOMIC_SEQ_CST);
    }

    return prev;
}
/*Z 设置节点锁需求标志，从头前插入到锁队列，待原队列锁均释放后，函数返回继续执行，相当于获取了锁 */
static inline void FORCE_INLINE clh_lock_acquire(word_t cpu, bool_t irqPath)
{
    clh_qnode_t *prev;
    big_kernel_lock.node_owners[cpu].node->value = CLHState_Pending;/*Z 设置需求标志。不会有竞争 */
    /*Z 原子地将指定cpu的节点状态，交给头，并返回头的 */
    prev = sel4_atomic_exchange(&big_kernel_lock.head, irqPath, cpu, __ATOMIC_ACQ_REL);
    /*Z 指定cpu节点链接到原来的头。如果这之前另外的核插足，队列是断裂的(因为next值还未正确设定)，但不影响正确执行 */
    big_kernel_lock.node_owners[cpu].next = prev;/* V 新头 的 next 指向 旧头部 */
    /*Z 等待原队列节点结束锁需求。注意：所有锁竞争者都是在这里等待。。。 */
    /* We do not have an __atomic_thread_fence here as this is already handled by the
     * atomic_exchange just above */
    while (big_kernel_lock.node_owners[cpu].next->value != CLHState_Granted) {/*V 循环等待原头释放锁 */
        /* As we are in a loop we need to ensure that any loads of future iterations of the
         * loop are performed after this one */
        __atomic_thread_fence(__ATOMIC_ACQUIRE);
        if (clh_is_ipi_pending(cpu)) {
            /* we only handle irq_remote_call_ipi here as other type of IPIs
             * are async and could be delayed. 'handleIPI' may not return
             * based on value of the 'irqPath'. */
            handleIPI(CORE_IRQ_TO_IRQT(cpu, irq_remote_call_ipi), irqPath);
            /* We do not need to perform a memory release here as we would have only modified
             * local state that we do not need to make visible */
        }
        arch_pause();
    }
    /*Z GCC内建函数，相当于linux的smp_load_acquire() */
    /* make sure no resource access passes from this point */
    __atomic_thread_fence(__ATOMIC_ACQUIRE);
}
/*Z 释放锁 */
static inline void FORCE_INLINE clh_lock_release(word_t cpu)
{
    /* make sure no resource access passes from this point */
    __atomic_thread_fence(__ATOMIC_RELEASE);

    big_kernel_lock.node_owners[cpu].node->value = CLHState_Granted;/*Z 释放锁（给队列中的前者一个继续执行的信号）*/
    big_kernel_lock.node_owners[cpu].node = /*Z 本节点状态指针移动到下一个节点（状态一定为无锁需求的）*/
        big_kernel_lock.node_owners[cpu].next;
}
/*Z 当前cpu是否在内核锁等待队列中 */
static inline bool_t FORCE_INLINE clh_is_self_in_queue(void)
{
    return big_kernel_lock.node_owners[getCurrentCPUIndex()].node->value == CLHState_Pending;
}
/*Z 当前cpu等待直至获取锁（等待期间可能处理IPI） */
#define NODE_LOCK(_irqPath) do {                         \
    clh_lock_acquire(getCurrentCPUIndex(), _irqPath);    \
} while(0)
/*Z 释放当前cpu持有的锁(应该是在确定持有锁的情况下调用) */
#define NODE_UNLOCK do {                                 \
    clh_lock_release(getCurrentCPUIndex());              \
} while(0)                                                           /* V do while 说明只执行一次，先do再while判断 */
/*Z 如果条件成立，当前cpu等待直至获取锁（等待期间可能处理IPI） */
#define NODE_LOCK_IF(_cond, _irqPath) do {               \
    if((_cond)) {                                        \
        NODE_LOCK(_irqPath);                             \
    }                                                    \
} while(0)
/*Z 如果当前cpu持有锁，则释放锁 */
#define NODE_UNLOCK_IF_HELD do {                         \
    if(clh_is_self_in_queue()) {                         \
        NODE_UNLOCK;                                     \
    }                                                    \
} while(0)

#else
#define NODE_LOCK(_irq) do {} while (0)
#define NODE_UNLOCK do {} while (0)
#define NODE_LOCK_IF(_cond, _irq) do {} while (0)
#define NODE_UNLOCK_IF_HELD do {} while (0)
#endif /* ENABLE_SMP_SUPPORT */

#define NODE_LOCK_SYS NODE_LOCK(false)  /*Z 当前cpu等待直至获取锁(等待期间可能处理IPI)。非中断场合使用 */
#define NODE_LOCK_IRQ NODE_LOCK(true)   /*Z 当前cpu等待直至获取锁(等待期间可能处理IPI)。中断场合使用 */
#define NODE_LOCK_SYS_IF(_cond) NODE_LOCK_IF(_cond, false)
#define NODE_LOCK_IRQ_IF(_cond) NODE_LOCK_IF(_cond, true)

